// pocketWin32.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "pocketWin32.h"
#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#include <winuser.h>
#include <stdlib.h>
#include <iostream>
#include "PDAserver.h"
#include "PDAfederate.h"
#include <afxsock.h>		// MFC socket extensions
#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions

#include <atlconv.h>

#include "PDAfederateAmbassador.h"
#include "RTIcommand.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE			g_hInst;			// current instance
HWND				g_hWndMenuBar;		// menu bar handle
HWND				hwdWin;
RECT				rectTextArea;
HDC					hdcWin;
LPWSTR				pszText = NULL;		// whole text

// Forward declarations of functions included in this code module:
ATOM			MyRegisterClass(HINSTANCE, LPTSTR);
BOOL			InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

using namespace rti1516;
PDAfederateAmbassador fAmb;

// Comment this out to disable the shutdown-delay functionality.
#define SHUTDOWN_DELAY

const int kBufferSize = 1024;
char acReadBuffer[kBufferSize];
void appendText(const wchar_t* text);

#if defined(SHUTDOWN_DELAY)
// How long to wait after we do the echo before shutting the connection
// down, to give the user time to start other clients, for testing 
// multiple simultaneous connections.
const int kShutdownDelay = 3;
#endif
        

////////////////////////////////////////////////////////////////////////
// Prototypes

u_long LookupAddress(const char* pcHost);
SOCKET EstablishConnection(u_long nRemoteAddr, u_short nPort);
bool SendMsg(SOCKET sd);
int ReadReply(SOCKET sd);


//// DoWinsock /////////////////////////////////////////////////////////
// The module's driver function -- we just call other functions and
// interpret their results.

int DoWinsock(const char* pcHost, int nPort)
{
    // Find the server's address
    //cout << "Looking up address..." << flush;
    u_long nRemoteAddress = LookupAddress(pcHost);
    if (nRemoteAddress == INADDR_NONE) {
        //cerr << endl << WSAGetLastErrorMessage("lookup address") << 
        //        endl;
		//AfxMessageBox(L"lookup address failed", MB_OK, 3);
		appendText(L"lookup address failed");
        return 3;
    }
    in_addr Address;
    memcpy(&Address, &nRemoteAddress, sizeof(u_long)); 
    //cout << inet_ntoa(Address) << ":" << nPort << endl; 

    // Connect to the server
    //cout << "Connecting to remote host..." << flush;
    SOCKET sd = EstablishConnection(nRemoteAddress, htons(nPort));
    if (sd == INVALID_SOCKET) {
        //cerr << endl << WSAGetLastErrorMessage("connect to server") << 
        //        endl;
        return 3;
    }
    //cout << "connected, socket " << sd << "." << endl;
AfxMessageBox(L"connected");
    // Send the echo packet to the server
    //cout << "Sending echo packet (" << strlen(kpcEchoMessage) << " bytes)..." << flush;
    int nBytes;
//	net::PDAserver server;
	
    while (SendMsg(sd) && false) {
		//cout << endl;
		if ((nBytes = ReadReply(sd)) > 0) {
			if(wcscmp(CA2W(acReadBuffer), L"objectInstanceNameReservationSucceeded") == 0) 
			{
				fAmb.objectInstanceNameReservationSucceeded(L"objectInstanceNameReservationSucceeded");
			} 
			else if (wcscmp(CA2W(acReadBuffer), L"discoverObjectInstance") == 0)
			{
				fAmb.discoverObjectInstance(L"discoverObjectInstance");
			}
			else
			{
				fAmb.reflectAttributeValues(L"reflectAttributeValues");
			}

			
		//	appendText(CA2W(acReadBuffer));
		}
		else if (nBytes == 0) {
			appendText(L"Server unexpectedly closed the connection");
		}
		else {
			/*cerr << endl << WSAGetLastErrorMessage("read reply") <<
					endl;*/
			appendText(L"Error occured");
			return 3;
		}
	}

#if defined(SHUTDOWN_DELAY)
    // Delay for a bit, so we can start other clients.  This is strictly
    // for testing purposes, so you can convince yourself that the 
    // server is handling more than one connection at a time.
   /* cout << "Will shut down in " << kShutdownDelay << 
            " seconds... (one dot per second): " << flush;*/
    for (int i = 0; i < kShutdownDelay; ++i) {
        Sleep(1000);
        //cout << '.' << flush;
    }
    //cout << endl;
#endif

    //// Shut connection down
    //cout << "Shutting connection down..." << flush;
    //if (ShutdownConnection(sd)) {
    //    //cout << "Connection is down." << endl;
    //}
    //else {
    //    //cout << endl << WSAGetLastErrorMessage("Shutdown connection") <<
    //    //        endl;
    //}

    //cout << "All done!" << endl;

    return 0;
}


//// LookupAddress /////////////////////////////////////////////////////
// Given an address string, determine if it's a dotted-quad IP address
// or a domain address.  If the latter, ask DNS to resolve it.  In
// either case, return resolved IP address.  If we fail, we return
// INADDR_NONE.

u_long LookupAddress(const char* pcHost)
{
    u_long nRemoteAddr = inet_addr(pcHost);
    if (nRemoteAddr == INADDR_NONE) {
        // pcHost isn't a dotted IP, so resolve it through DNS
        hostent* pHE = gethostbyname(pcHost);
        if (pHE == 0) {
            return INADDR_NONE;
        }
        nRemoteAddr = *((u_long*)pHE->h_addr_list[0]);
    }

    return nRemoteAddr;
}


//// EstablishConnection ///////////////////////////////////////////////
// Connects to a given address, on a given port, both of which must be
// in network byte order.  Returns newly-connected socket if we succeed,
// or INVALID_SOCKET if we fail.

SOCKET EstablishConnection(u_long nRemoteAddr, u_short nPort)
{
    // Create a stream socket
    SOCKET sd = socket(AF_INET, SOCK_STREAM, 0);
    if (sd != INVALID_SOCKET) {
        sockaddr_in sinRemote;
        sinRemote.sin_family = AF_INET;
        sinRemote.sin_addr.s_addr = nRemoteAddr;
        sinRemote.sin_port = nPort;
        if (connect(sd, (sockaddr*)&sinRemote, sizeof(sockaddr_in)) ==
                SOCKET_ERROR) {
            sd = INVALID_SOCKET;
        }
    }

    return sd;
}


//// SendEcho //////////////////////////////////////////////////////////
// Sends the echo packet to the server.  Returns true on success,
// false otherwise.

bool SendMsg(SOCKET sd)
{
    // Send the string to the server
    if (send(sd, "msg", 3, 0) != SOCKET_ERROR) {
		return true;
	}
	else {
        return false;
    }
}


//// ReadReply /////////////////////////////////////////////////////////
// Read the reply packet and check it for sanity.  Returns -1 on 
// error, 0 on connection closed, > 0 on success.

int ReadReply(SOCKET sd)
{
    // Read reply from server
    
    int nTotalBytes = 0;
	// TODO: expected read byte count
    while (nTotalBytes < 10) {
        int nNewBytes = recv(sd, acReadBuffer + nTotalBytes, 
                kBufferSize - nTotalBytes, 0);
        if (nNewBytes == SOCKET_ERROR) {
            return -1;
        }
        else if (nNewBytes == 0) {
            appendText(L"Connection closed by peer.");
            return 0;
        }
		
//		appendText(ATL::CA2W(acReadBuffer));
        nTotalBytes += nNewBytes;
    }

    return nTotalBytes;
}

void listenServer()
{
	LPWSTR szIp = (wchar_t*)malloc(sizeof(wchar_t) * 25);	
	LoadString(g_hInst, IDS_SRVIP, szIp, 25); 
	
   // const char* pcHost = "192.168.2.2";
    int nPort = 3000;
	//AfxMessageBox(szIp);
    // Start Winsock up
    WSAData wsaData;
	int nCode;
 //   if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) { }
    // Call the main example routine.
 //   int retval = DoWinsock(ATL::CW2A(szIp), nPort);

	//net::PDAserver server;
	//net::RTIcommand comm("command");
	//server.sendCommand(&comm);
	

	rti1516::PDAfederate federate;
	federate.start();

    // Shut Winsock back down and take off.
    WSACleanup();
}

volatile static int nLines = 0;
void appendText(const wchar_t* text)
{
	LPWSTR pszNew;
	LPWSTR pszTemp;

	nLines++;
	if(nLines == 11)
	{
		pszText = (LPWSTR) VirtualAlloc((LPVOID) NULL, 
		(DWORD) (1), MEM_COMMIT, 
		PAGE_READWRITE);

		wcscpy(pszText, L" ");
	}

	pszNew = (LPWSTR) VirtualAlloc((LPVOID) NULL, 
		(DWORD) (wcslen(text)), MEM_COMMIT, 
		PAGE_READWRITE); 
	wcscpy(pszNew, text);

	pszTemp = (LPWSTR) VirtualAlloc((LPVOID) NULL, 
		(DWORD) (wcslen(pszText) + wcslen(pszNew) + 1), MEM_COMMIT, 
		PAGE_READWRITE); 

	wcscpy(pszTemp, pszText);
	wcscat(pszTemp, L"\n");
	wcscat(pszTemp, pszNew);

	pszText = pszTemp;		// save new text

	VirtualFree(pszNew, 0, MEM_RELEASE);
	SendMessage (hwdWin, WM_PAINT, 0, 0);
}

int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrevInstance,
				   LPTSTR    lpCmdLine,
				   int       nCmdShow)
{
	MSG msg;

	SHInitExtraControls();
	if (!AfxSocketInit())
	{
		AfxMessageBox(104);
		return FALSE;
	}

	// Perform application initialization:
	if (!InitInstance(hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	listenServer();

	HACCEL hAccelTable;
	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_POCKETWIN32));
	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{	
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
	WNDCLASS wc;

	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hInstance;
	wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_POCKETWIN32));
	wc.hCursor       = 0;
	wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = 0;
	wc.lpszClassName = szWindowClass;

	return RegisterClass(&wc);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd;
	TCHAR szTitle[MAX_LOADSTRING];		// title bar text
	TCHAR szWindowClass[MAX_LOADSTRING];	// main window class name

	g_hInst = hInstance; // Store instance handle in our global variable

	// SHInitExtraControls should be called once during your application's initialization to initialize any
	// of the device specific controls such as CAPEDIT and SIPPREF.
	SHInitExtraControls();

	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 
	LoadString(hInstance, IDC_POCKETWIN32, szWindowClass, MAX_LOADSTRING);

	//If it is already running, then focus on the window, and exit
	hWnd = FindWindow(szWindowClass, szTitle);	
	if (hWnd) 
	{
		// set focus to foremost child window
		// The "| 0x00000001" is used to bring any owned windows to the foreground and
		// activate them.
		SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
		return 0;
	} 

	if (!MyRegisterClass(hInstance, szWindowClass))
	{
		return FALSE;
	}

	hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	if (!hWnd)
	{
		return FALSE;
	}

	// When the main window is created using CW_USEDEFAULT the height of the menubar (if one
	// is created is not taken into account). So we resize the window after creating it
	// if a menubar is present
	if (g_hWndMenuBar)
	{
		RECT rc;
		RECT rcMenuBar;

		GetWindowRect(hWnd, &rc);
		GetWindowRect(g_hWndMenuBar, &rcMenuBar);
		rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);

		MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);
	}

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);


	return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	static SHACTIVATEINFO s_sai;
	
	RECT rt;
	switch (message) 
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam); 
		wmEvent = HIWORD(wParam); 
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_HELP_ABOUT:
			DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About);
			break;
		case IDM_OK:
			SendMessage (hWnd, WM_CLOSE, 0, 0);				
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_CREATE:
		SHMENUBARINFO mbi;

		memset(&mbi, 0, sizeof(SHMENUBARINFO));
		mbi.cbSize     = sizeof(SHMENUBARINFO);
		mbi.hwndParent = hWnd;
		mbi.nToolBarId = IDR_MENU;
		mbi.hInstRes   = g_hInst;

		if (!SHCreateMenuBar(&mbi)) 
		{
			g_hWndMenuBar = NULL;
		}
		else
		{
			g_hWndMenuBar = mbi.hwndMB;
		}

		// Initialize the shell activate info structure
		memset(&s_sai, 0, sizeof (s_sai));
		s_sai.cbSize = sizeof (s_sai);

		// initialize the string contains whole text
		pszText = (LPWSTR) VirtualAlloc((LPVOID) NULL, 
			(DWORD) (1), MEM_COMMIT, 
			PAGE_READWRITE); 
		wcscpy(pszText, L"\0");

		break;
	case WM_PAINT:
		hwdWin = hWnd;
		hdc = BeginPaint(hWnd, &ps);
		hdcWin = hdc;		// save hdc
		GetClientRect(hWnd, &rt);
		rectTextArea = rt;	// save rectangle
		BeginPaint(hwdWin, &ps);
		DrawText(hdcWin, pszText, wcslen(pszText), &rectTextArea, DT_LEFT);
		EndPaint(hwdWin, &ps);		
		break;
	case WM_DESTROY:
		CommandBar_Destroy(g_hWndMenuBar);
		PostQuitMessage(0);
		break;
	case WM_ACTIVATE:
		// Notify shell of our activate message
		SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
		break;
	case WM_SETTINGCHANGE:
		SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_INITDIALOG:
		{
			// Create a Done button and size it.  
			SHINITDLGINFO shidi;
			shidi.dwMask = SHIDIM_FLAGS;
			shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
			shidi.hDlg = hDlg;			
			SHInitDialog(&shidi);
		}
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK)
		{
			// TODO get ip
			EndDialog(hDlg, LOWORD(wParam));
			return TRUE;
		}
		break;

	case WM_CLOSE:
		EndDialog(hDlg, message);
		return TRUE;

#ifdef _DEVICE_RESOLUTION_AWARE
	case WM_SIZE:
		{
			DRA::RelayoutDialog(
				g_hInst, 
				hDlg, 
				DRA::GetDisplayMode() != DRA::Portrait ? MAKEINTRESOURCE(IDD_ABOUTBOX_WIDE) : MAKEINTRESOURCE(IDD_ABOUTBOX));
		}
		break;
#endif
	}
	return (INT_PTR)FALSE;
}

